function Observer(data) {
  this.data = data;
  this.walk(data);
}

Observer.prototype = {
  walk: function (data) {
    var me = this;
    // 提取data中所有属性进行遍历
    Object.keys(data).forEach(function (key) {
      // me.convert(key, data[key]);
      // 将data原数据的所有属性，进行重新定义，将其定义成响应式的数据
      me.defineReactive(data, key, data[key]);
    });
  },
  //   convert: function (key, val) {
  //     this.defineReactive(this.data, key, val);
  //   },

  defineReactive: function (data, key, val) {
    // data所有属性都一个自己的dep对象
    var dep = new Dep();

    // 递归调用：让data中对象数据里面的属性也要变成响应式
    // 让所有data数据都变成响应式
    var childObj = observe(val);

    // 定义响应式
    // 将data原数据的所有属性，进行重新定义
    Object.defineProperty(data, key, {
      enumerable: true, // 可枚举
      configurable: false, // 不能再define
      get: function () {
        if (Dep.target) {
          // 建立响应式联系: 互相保存
          // dep保存watcher，watcher保存dep
          // dep保存watcher （响应式数据 -> dep -> watcher -> cb）
          // watcher保存dep （防止dep重复保存相同的watcher）
          dep.depend();
        }
        return val;
      },
      set: function (newVal) {
        if (newVal === val) {
          return;
        }
        // 更新值
        val = newVal;
        // 新的值是object的话，进行监听
        // 将新值也要变成响应式
        childObj = observe(newVal);

        // 触发用户界面的更新
        // 通知订阅者
        dep.notify();
      },
    });
  },
};

function observe(value, vm) {
  // 过滤非对象数据
  if (!value || typeof value !== "object") {
    return;
  }

  // object或array
  return new Observer(value);
}

var uid = 0;

function Dep() {
  // 唯一标识
  this.id = uid++;
  // 存储watcher的容器
  this.subs = [];
}

Dep.prototype = {
  // 添加watcher的方法
  addSub: function (sub) {
    this.subs.push(sub);
  },

  depend: function () {
    // Dep.target --> watcher
    // this --> dep
    // watcher.addDep(dep)
    Dep.target.addDep(this);
  },

  removeSub: function (sub) {
    var index = this.subs.indexOf(sub);
    if (index != -1) {
      this.subs.splice(index, 1);
    }
  },

  notify: function () {
    this.subs.forEach(function (sub) {
      // sub --> watcher
      // watcher.update()
      sub.update();
    });
  },
};

Dep.target = null;
